fix(viewer): keep deep-linked inline comment thread aligned to its highlight#570
Merged
Conversation
…ghlight An inline comment thread is pinned to its highlighted passage in the right-margin column via an article-relative offset (`activeTop`) that only recomputed on the article's ResizeObserver. That observer reports the article's box size, not the highlight's position within it — so when content *above* the highlight reflowed (a web-font swap on cold load, or a late image/diagram) the highlight slid down without the article resizing, the offset went stale, and the thread floated hundreds of px above its passage. Fix the trigger at the right altitude with two complementary mechanisms: - `observeMove` — a small IntersectionObserver primitive (dynamic rootMargin, re-arming on each move; a dependency-free MIT port of Floating UI's autoUpdate layoutShift) that fires when an anchored element *moves* even though its own box is unchanged. Exposed as `useElementMove` and wired into the margin pin (the active highlight) and `useAnchorOffset` (so the narrow-screen comment popover re-aligns too). Catches any reflow *after* the thread exists — live-reload re-wraps, late images/diagrams. The move getter reads `comments.items` so it re-subscribes to the fresh wrapper after a reconcile rebuilds highlights. - A one-shot re-measure right after a deep-link reveal. On a cold load the stranding reflow happens during the load burst — before observeMove is watching, and while the highlight is still off-screen (so observeMove would only correct it via its ~1s poll). By reveal time the reflow has settled, so a single re-measure there aligns it immediately instead of after a visible ~1s delay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Opening an inline-comment deep link (
#comment-<id>) could leave the comment thread in the right-margin column pinned hundreds of pixels above its highlighted passage. The thread is positioned by an article-relative offset (activeTop) that only recomputed on the article's ResizeObserver — which reports the article's box size, not the highlight's position within it. So when content above the highlight reflowed (a web-font swap on cold load, or a late image/diagram), the highlight slid down without the article resizing, the offset went stale, and the thread floated away from its passage.A second symptom on hard refresh: the thread appeared instantly but mispositioned, then snapped into place ~1s later.
Fix
Trigger the re-measure at the right altitude, with two complementary mechanisms:
observeMove— a smallIntersectionObserverprimitive (dynamicrootMargin, re-arming on each move; a dependency-free MIT port of Floating UI'sautoUpdate({layoutShift})) that fires when an anchored element moves even though its own box is unchanged. Exposed asuseElementMove, wired into the margin pin (active highlight) anduseAnchorOffset(so the narrow-screen comment popover re-aligns too). Catches any reflow after the thread exists — live-reload re-wraps, late images/diagrams. The move getter readscomments.itemsso it re-subscribes to the fresh wrapper after a reconcile rebuilds highlights.observeMoveis watching, and while the highlight is still off-screen (soobserveMovewould only correct it via its ~1s poll). By reveal time the reflow has settled, so a single re-measure there aligns it immediately instead of after a visible ~1s delay.Verification
observeMove/useElementMove/useAnchorOffset-move coverage), 45 comment e2e, typecheck + lint all green; no regression in existing deep-link/alignment tests.Follow-up (not in this PR)
The upstream trigger is unmanaged web-font FOUT (
fonts.cssshipsfont-display: swapwith no metric-matched fallback) plus dimensionless<img>. Addingsize-adjust/ascent-overridefallback faces is an independent CLS win that would shrink the deep-link settle to a rare belt-and-suspenders path — tracked separately.🤖 Generated with Claude Code